package com.googlecode.restitory.gae.filter;

import java.io.IOException;
import java.util.Enumeration;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import com.googlecode.mycontainer.commons.io.IOUtil;
import com.googlecode.restitory.gae.BinaryContent;
import com.googlecode.restitory.gae.GAERequestUtil;
import com.googlecode.restitory.gae.NamePair;
import com.googlecode.restitory.gae.Request;
import com.googlecode.restitory.gae.Response;
import com.googlecode.restitory.gae.TextContent;
import com.googlecode.restitory.gae.Type;
import com.googlecode.restitory.gae.hook.DefaultRequestService;

public class RequestServiceFilter implements Filter {

	@Override
	public void init(FilterConfig config) throws ServletException {

	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		Request req = createRequest((HttpServletRequest) request);

		DefaultRequestService c = new DefaultRequestService();
		Response resp = c.execute(req);
		if (resp != null) {
			writeResp(resp, (HttpServletResponse) response);
			return;
		}
		chain.doFilter(request, response);
	}

	private void writeResp(Response resp, HttpServletResponse response) {
		try {
			String message = resp.getMessage();
			if (message == null) {
				response.setStatus(resp.getCode());
			} else {
				response.sendError(resp.getCode(), message);
			}
			setHeaders(resp, response);

			if (resp.getContentType() != null) {
				if (GAERequestUtil.isBinary(resp.getContentType())) {
					BinaryContent content = (BinaryContent) resp.getContent();
					response.getOutputStream().write(content.getValue());
				} else {
					TextContent content = (TextContent) resp.getContent();
					response.getWriter().write(content.getValue());
				}
			}

		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	private void setHeaders(Response resp, HttpServletResponse response) {
		List<NamePair> pairs = resp.getHeaders().getPairs();
		for (NamePair namePair : pairs) {
			String name = namePair.getName();
			if (!name.equals("Content-Type")) {
				response.addHeader(name, namePair.getValue());
			}
		}
		response.setContentType(resp.getContentType());
	}

	private Request createRequest(HttpServletRequest request) {
		try {
			Request ret = new Request();
			ret.setType(Type.valueOf(request.getMethod().toUpperCase()));
			ret.setPath(request.getRequestURI());
			setParams(ret, request);
			setHeaders(ret, request);

			if (request.getContentType() != null) {
				if (GAERequestUtil.isBinary(request.getContentType())) {
					byte[] bytes = IOUtil.readAll(request.getInputStream());
					ret.setContent(bytes);
				} else {
					char[] chars = IOUtil.readAll(request.getReader());
					ret.setContent(chars);
				}
			}

			return ret;
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	@SuppressWarnings({ "unchecked" })
	private void setParams(Request ret, HttpServletRequest request) {
		Set<Entry<String, String[]>> set = request.getParameterMap().entrySet();
		for (Entry<String, String[]> entry : set) {
			String key = entry.getKey();
			String[] array = entry.getValue();
			for (String value : array) {
				ret.getParams().add(key, value);
			}
		}
	}

	@SuppressWarnings("unchecked")
	private void setHeaders(Request req, HttpServletRequest request) {
		Enumeration<String> names = request.getHeaderNames();
		while (names.hasMoreElements()) {
			String name = names.nextElement();
			Enumeration<String> headers = request.getHeaders(name);
			while (headers.hasMoreElements()) {
				String header = headers.nextElement();
				req.getHeaders().add(name, header);
			}
		}
	}

	@Override
	public void destroy() {

	}

}
